#include "entity.h"
#include "vec.h"

#define SPRITE_DMA_CHANNEL 1
#define ENTITY_HIT_RADIUS 10

#define BYTES_PER_COLOR_TILE 32
#define ANIMATE_STEPRIGHT(index, bytes) (index += bytes)
#define ANIMATE_STEPLEFT(index, bytes) (index -= bytes)

int Entity::count = 0;
int Entity::paletteCount = 0;

Entity::Entity(int xx, int yy, int p, u16* main, u16* sub)
  : x(xx), y(yy), mainGfx(main), subGfx(sub), palette(p),
    vel(0, 0), accel(0, 0) {
  num = count++;
  health = 100;
  gotHit = false;
  stepped = 0;
}

void Entity::move(int xx, int yy) {
  if (health <= 0) {
    oamSet(&oamMain, num, xx, yy-32, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           mainGfx, -1, false, true, false, false, false);
    oamSet(&oamSub, num, xx, yy + SCREEN_HEIGHT, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           subGfx, -1, false, true, false, false, false);
    return;
  }

  //x = xx;
  //y = yy;
  if (y < 0) {
    oamSet(&oamMain, num, xx, yy - 32, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           mainGfx, -1, false, true, false, false, false);
    oamSet(&oamSub, num, xx, yy + SCREEN_HEIGHT, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           subGfx, -1, false, false, false, false, false);
  } else {
    oamSet(&oamMain, num, xx, yy - 32, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           mainGfx, -1, false, false, false, false, false);
    oamSet(&oamSub, num, xx, yy + SCREEN_HEIGHT, 0, palette,
           SpriteSize_32x32, SpriteColorFormat_16Color,
           subGfx, -1, false, true, false, false, false);
  }
}

int Entity::initGfx(const void* palette, int palLen,
                    const void* tiles, int len,
                    u16** main, u16** sub) {
  int pal = paletteCount++;
  *main = oamAllocateGfx(&oamMain,
                         SpriteSize_32x64,
                         SpriteColorFormat_16Color);
  dmaCopyHalfWords(SPRITE_DMA_CHANNEL, palette,
                   &SPRITE_PALETTE[pal * 16], palLen);
  dmaCopyHalfWords(SPRITE_DMA_CHANNEL, tiles, *main, len);
  *sub = oamAllocateGfx(&oamSub,
                        SpriteSize_32x64,
                        SpriteColorFormat_16Color);
  dmaCopyHalfWords(SPRITE_DMA_CHANNEL, palette,
                   &SPRITE_PALETTE_SUB[pal * 16], palLen);
  dmaCopyHalfWords(SPRITE_DMA_CHANNEL, tiles, *sub, len);
  return pal;
}

float Entity::collisionTime(int rayx, int rayy, float dirx, float diry) {
  if (health <= 0) {
    return -1;
  }
	animate_stepleft();

  Vec ray_start(rayx, rayy);
  Vec ray_dir(dirx, diry);
  Vec center(x + 16, y + 16);
  Vec center_to_ray = center.to(ray_start);

  float a = ray_dir.dot(ray_dir);
  float b = 2 * center_to_ray.dot(ray_dir);
  float c = center_to_ray.dot(center_to_ray) - pow(ENTITY_HIT_RADIUS, 2);

  float disc = pow(b, 2) - 4*a*c;
  if (disc < 0) {
    return -1;
  }
  float t1 = (-b + sqrt(disc)) / (2*a);
  float t2 = (-b - sqrt(disc)) / (2*a);
  if (t1 < t2) {
    return t1;
  }
  return t2;
}

void Entity::getHit(int laserPower) {
  health -= ((laserPower / 20) + 1);
  gotHit = true;
  animate_stepright();
}

void Entity::killThoseWhoWanderAstray() {
  if(x < -32 || x > SCREEN_WIDTH ||
     y < -SCREEN_HEIGHT - 32 || y > SCREEN_HEIGHT + 32) {
    health = 0;
  }
}

// Once again, it's 4AM, that's my excuse.
void Entity::takeThisGiftSoThatYouMayLiiiive(int playerKills) {
  int direction = 0; /*rand() % 4;*/
  if (playerKills > 50 && rand() % playerKills > 50) {
    direction = 1;
  }
  switch(direction) {
  case 0:  // top
    y = -SCREEN_HEIGHT -32;
    x = rand() % SCREEN_WIDTH;
    vel = Vec(0, 0.1 + (playerKills / 100.0));
    break;
  case 1:  // bottom
    y = SCREEN_HEIGHT + 32;
    x = rand() % SCREEN_WIDTH;
    vel = Vec(0, -0.1);
    break;
  case 2:  // right
    x = SCREEN_WIDTH;
    y = rand() % (2 * SCREEN_HEIGHT + 32) - SCREEN_HEIGHT;
    vel = Vec(-0.1, 0);
    break;
  case 3:  // left
    x = -32;
    y = rand() % (2 * SCREEN_HEIGHT + 32) - SCREEN_HEIGHT;
    vel = Vec(0.1, 0);
    break;
  }
  health = 30;
  accel = Vec(rand() % 100 - 50, rand() % 100 - 50).unit().scale(0.001);
}

void Entity::step() {
  x += vel.x;
  y += vel.y;

  //vel = vel.plus(accel);
}

int ReflectiveEntity::reflectAngle(int impactX, int impactY,
				   float dirx, float diry,
				   int* reflections) {
  Vec in(dirx, diry);
  Vec normal((float)impactX - (x + 16), (float)impactY - (y + 16));
  Vec out = in.reflect(normal);
  reflections[0] = out.angle();
  return 1;
}

int RedirectEntity::reflectAngle(int impactX, int impactY,
				 float dirx, float diry,
				 int* reflections) {
  Vec out(0, 1);
  reflections[0] = out.angle();
  return 1;
}

int BoringEntity::reflectAngle(int impactX, int impactY,
			       float dirx, float diry,
			       int* reflections) {
  return 0;
}

int SplitterEntity::reflectAngle(int impactX, int impactY,
				 float dirX, float dirY,
				 int* reflections) {
  Vec in(dirX, dirY);
  Vec out1(-in.y, in.x);
  Vec out2 = out1.scale(-1);
  reflections[0] = out1.plus(in).angle();
  reflections[1] = out2.plus(in).angle();
  return 2;
}

//animate functions
void Entity::animate_stepright() {
  if(stepped == 0) {
	ANIMATE_STEPRIGHT(mainGfx,256);
	ANIMATE_STEPRIGHT(subGfx, 256);
  }
  stepped = 1;
}

void Entity::animate_stepleft() {
  if(stepped == 1) {
	ANIMATE_STEPLEFT(mainGfx, 256);
	ANIMATE_STEPLEFT(subGfx, 256);
  }
  stepped = 0;
}